//
// OS_UNIX.h
// libprotea
// Tools Library for the Protea Project / Operating System unique code, specific to UNIX
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#ifndef __PROTEA_OS_UNIX_H__
#define __PROTEA_OS_UNIX_H__

//
// OS_UNIX.h contains definitions common to UNIX platforms
//

// Include Files
#include <sys/errno.h>
#include <unistd.h>
#include <sys/times.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pwd.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <pthread.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <termios.h>
#include <ctype.h>
#include <vector>

// File delimiter
#define OSFILEDELIM "/"

// Type Definitions
typedef int PORT_HANDLE;
typedef DIR* DIR_HANDLE_TYPE;
typedef fd_set port_set;
typedef struct timeval time_val;
typedef void* (*XCThreadProc)( void* );
typedef pthread_t THREAD_HANDLE;
typedef pthread_cond_t XC_EVENT_TYPE;
typedef PORT_HANDLE SOCKET;

// Const Defnitions
const long MAX_PATH = 255;
const long INVALID_SOCKET = -1;

// Breakpoints and memory checks - not implemented
#define BREAKPOINT
#define CHECKMEM

// Use for Checking Buffer Cleanup
#define CLEAR_BUFFER_COUNT { BufferClear(); XCDBStatement::counter = 0; }
#define CHECK_BUFFER_COUNT {\
	BufferDump( txnvars.myapp, transaction ); \
	if (XCDBStatement::counter != 0) {											\
		transaction_defn* txn_code = txnvars.myapp->GetTransaction( transaction->GetTransNumber() ); \
		int phase = transaction->GetPhase(); \
		cout << "UNRELEASED STATEMENTS: " << XCDBStatement::counter << " Txn=" << txn_code->trans_code << "  [TransID=" << transaction->GetTransID() << ",Phase=" << phase << "]" << endl;  \
/*		App->ToolsTrace( TRC_APPINTERFACE, TLV_HIGH, "*** Unreleased statements: %d  Txn=%s", XCDBStatement::counter, txn_code->trans_code ); */ \
	}; \
	BufferClear(); \
	XCDBStatement::counter = 0; \
};

// Debug Output
#define DEBUGOUT( x ) { printf( "%s\n", x ); }
#define OUTPUTFUNC( x ) { cout << x; }

// Register Signal Handlers
#define OSDEP_SIGKILL SIGKILL
#define OSDEP_SIGNAL( x, y ) { if ( signal( x, y ) == SIG_ERR ) cout << "Couldn't register signal handler!\n"; }

// files.h
#define OSDEP_READ_ONLY O_RDONLY
#define OSDEP_WRITE_ONLY O_WRONLY
#define OSDEP_READ_WRITE O_RDWR
#define	OSDEP_CREATE_NEW O_CREAT | O_EXCL
#define	OSDEP_CREATE_ALWAYS O_CREAT | O_TRUNC
#define	OSDEP_OPEN_EXISTING 0
#define	OSDEP_OPEN_ALWAYS O_CREAT
#define	OSDEP_SEEK_START SEEK_SET
#define	OSDEP_SEEK_CURRENT SEEK_CUR
#define OSDEP_SEEK_END SEEK_END

// process.h
#define THREAD_PROCEDURE void* 
#define OSDEP_CRITICAL_SECTION void*
#define OSDEP_MUTEX pthread_mutex_t
#define OSDEP_TLS_INDEX pthread_key_t
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun {
        int val;                    /* value for SETVAL */
        struct semid_ds *buf;       /* buffer for IPC_STAT, IPC_SET */
        unsigned short int *array;  /* array for GETALL, SETALL */
        struct seminfo *__buf;      /* buffer for IPC_INFO */
};
#endif

// service.h
#define OSDEP_SERVICE_FUNCTIONS
#define OSDEP_SERVICE_MAIN void* 
#define OSDEP_SERVICE_DISPATCH_FUNCTIONS 
#define DAEMON_SERVICE( classname, var, name, dispname, desc, deps ) \
	void DaemonCtlr( long code ); \
	classname var( name, dispname, desc, (void*)DaemonCtlr, deps );	\
	void DaemonCtlr( long code ) {}

// socket.h
#define SOCKET_ERROR -1

// AppInterface.h
#define APP_INSTALL_EXCEPTIONS
#define APP_INIT_DEBUG
#define APP_LOOPBACK_INIT XCPipeTransport* loopback; loopback = new XCPipeTransport( this, (PORT_HANDLE)in, (PORT_HANDLE)0, (XCMsgPort*)NULL );
#define APP_LOOPBACK_OUTPUT loopback->SetOutPipe( init.response );
#define APP_LOCK_CHILDLIST
#define APP_UNLOCK_CHILDLIST
#define APP_CREATE_CHILD_LISTEN 
#define APP_CONNECT_LISTEN 
#define APP_MESSAGE_THREAD MessageLoopThread( parm );

// message.h
#define LAST_READ_TYPE unsigned long

// printoutput.h
#define PRINT_OUTPUT_TO_FILE sprintf( file, "%s -fo %s %s %s", app->fop_location, input_file, options ? options : "", output_file ); \
		printf( "Execute FOP: %s\n", file ); \
		fop_result = system( file ); \
		if (fop_result == -1) \
			RAISEOSERROR(); \
		/* If printing... */ \
		if (print && fop_result == 0) { \
			if (style == PS_OUTPUT) { \
				if (app->printer) \
					sprintf( file, "cat %s | lpr -P %s", output_file, app->printer ); \
				else \
					sprintf( file, "cat %s | lpr", output_file ); \
				system( file ); \
			} else if (style == PDF_OUTPUT) { \
				if (app->printer) \
					sprintf( file, "pdf2ps %s pstemp ; cat pstemp | lpr -P %s", output_file, app->printer ); \
				else \
					sprintf( file, "pdf2ps %s pstemp ; cat pstemp | lpr", output_file ); \
				system( file ); \
				XCDeleteFile( "pstemp" ); \
			};\
		};

// entry.h
#define ENTRY_CRYPT_PROVIDER 
#define ENTRY_MAIN
#define ENTRY_INIT /* Default signal handlers */ \
	/* Ignore all sigpipe errors.  Protea should handle these through */ \
	/* error condition checks.  The problem is that under Linux, */ \
	/* it is delivered to the process, and not the thread.  This causes */ \
	/* problems as we cannot reliably handle the signal.  It is possible */ \
	/* to use the MSG_NOSIGNAL flag under Linux, and handle the signal */ \
	/* on other platforms */ \
	/* see:http://www.openldap.org/lists/openldap-devel/200308/msg00005.html */ \
	signal( SIGPIPE, SIG_IGN );
#define ENTRY_UNINIT 
#define ENTRY_THREAD_INIT 
#define ENTRY_THREAD_UNINIT 

// errorhandler.h
#define ERRHANDLER_SEH_INIT 
#define RAISEOSERROR() { long temp = errno; OUTPUTRAISE; ToolsRaiseOSError( temp, __FILE__, __LINE__, TOOLSAPP, NULL ); }
#define RAISEOSERROR_PARAM( parm ) { long temp = errno; OUTPUTRAISE; ToolsRaiseOSError( temp, __FILE__, __LINE__, TOOLSAPP, parm ); }

// stacktrace.h
#define STACKTRACE_PERF_FREQ 
#define STACKTRACE_COUNT_TYPE timeval
#ifdef PENTIUM
#	define STACKTRACE_TIME_TYPE unsigned long long
#	define STACKTRACE_FREQ_TYPE static long
#else
#	define STACKTRACE_TIME_TYPE timeval
#	define STACKTRACE_FREQ_TYPE void*
#endif
#define USECSPERSEC 1000000
#define STACKTRACE_ENTER gettimeofday( &temp->start, NULL );
#define STACKTRACE_LEAVE gettimeofday( &temp->end, NULL ); \
			timeval tresult; \
			tresult.tv_sec = temp->end.tv_sec - temp->start.tv_sec; \
			tresult.tv_usec = temp->end.tv_usec - temp->start.tv_usec; \
			if (tresult.tv_usec < 0) { \
				tresult.tv_sec--; \
				tresult.tv_usec += USECSPERSEC; \
			}; \
			timing = tresult.tv_sec * USECSPERSEC + tresult.tv_usec;

// hash.h
#define HASH_HANDLE void*
#define HASH_DESTROY 
#define HASH_INIT digest[0] = 0x67452301L; digest[1] = 0xEFCDAB89L; digest[2] = 0x98BADCFEL; digest[3] = 0x10325476L; digest[4] = 0xC3D2E1F0L; little_endian = false;
#define SHA_UPDATE
#define HASH_UPDATE 
#define SHA_FINAL
#define HASH_FINAL 

// DBInterface.h
#define DBINTERFACE_EXTENSION ".xdl"
#define INC_STMT_COUNTER XCDBStatement::counter++;
#define DEC_STMT_COUNTER XCDBStatement::counter--;

// GCS
#define GCS_CTRL_HANDLER
#define GCS_SIG_HANDLER OSDEP_SIGNAL( SIGCHLD, catchchildren );

// XMLINT
#define XMLINT_CTRL_HANDLER

// PREP
#define PREP_SIG_HANDLER void sighandler( int signum ) { \
	printf( "Aborting...  signal %d", signum ); \
	XCDeleteFile( lockfilename ); \
	raise( SIGKILL ); \
};

// LOADAPPDIC
#define LOADAPPDIC_GETLINE const long STDIN = 0; \
	struct termios oldios, newios; \
	if (password_char) { \
		/* Turn off echo */ \
		int nread; \
		/* Turn echoing off and fail if we can't. */ \
		if (tcgetattr(STDIN, &oldios) != 0) { \
			/* Ignore "Inappropriate ioctl for device" error */ \
			if (errno != 25) \
				RAISEOSERROR(); \
		}; \
		newios = oldios; \
		newios.c_lflag &= ~ECHO; \
		if (tcsetattr (STDIN, TCSAFLUSH, &newios) != 0) { \
			/* Ignore "Inappropriate ioctl for device" error */ \
			if (errno != 25) \
				RAISEOSERROR(); \
		}; \
	}; \
	/* Reset the password field */ \
	*szPassword = 0; \
	/* Obtain user input */ \
	fgets( szPassword, max_length, stdin ); \
	char* newline_char = strchr( szPassword, '\n' ); \
	if (newline_char) \
		*newline_char = 0; \
	if (password_char) { \
		/* Restore terminal. */ \
		(void) tcsetattr (STDIN, TCSAFLUSH, &oldios); \
	}; \
	RETURNFUNC( result );

#endif
